document.querySelector('#app')
与document.getElementById('app')
有什么区别?<ul> <li>111</li> <li>222</li> <li>333</li> </ul> //`document.querySelector('#app')`选出的元素是静态的: <script> var ul = document.querySelector('ul'); var list = ul.querySelectorAll('li'); //又创建了5个新的li,添加在ul列表上。 for (var i = 0; i < 5; i++) { ul.appendChild(document.createElement('li')); } console.log(list.length) // 仍然是3 </script> // `document.getElementById('app')`是动态的 <script> var ul = document.getElementsByTagName('ul')[0]; var list = ul.getElementsByTagName('li'); //又创建了5个新的li,添加在ul列表上。 for (var i = 0; i < 5; i++) { ul.appendChild(document.createElement('li')); } console.log(list.length) // 变成了8 </script>
同理可推广到:
document.querySelectorAll
、document.getElementsByClassName
、document.getElementsByTagName
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
dom
对象与jQuery
对象互转var pDom = document.querySelectorAll('p') var $pDom = $('p') // dom 转 jQuery var $pDom1 = $(pDom) // jQuery 转 dom,分两种情况 // 情况一:jQuery 对象只含有一个元素,例如 $('#p') var pDom1 = $pDom[0] // 方法1 var pDom2 = $pDom.get(0) // 方法2 // 情况二:jQuery 对象含有多个元素,例如 $('p') var pDom1 = [] var len = $pDom.length for(var i = 0; i < len; i++) { pDom1.push($pDom[i]) }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
实现动画效果
// CSS .box { position: relative; // relative、absolute、fixed之一 animation: mymove 5s infinite; } @keyframes mymove { from { left: 0; } to { left: 200px; } } // js —— 定时器 let box = document.querySelector('.box') let flag = true let left = 0 setInterval(() => { left === 0 ? flag = true : (left === 100 ? flag = false : '') flag ? box.style.left = `${left++}px` : box.style.left = `${left--}px` }, 1000 / 60) // js —— requestAnimationFrame let box = document.getElementById(".box"); let flag = true; let left = 0; function render() { left == 0 ? flag = true : left == 100 ? flag = false : ''; flag ? box.style.left = ` ${left++}px` : box.style.left = ` ${left--}px`; } let requestAnimFrame = (function(){ //兼容性处理 return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback){ window.setTimeout(callback, 1000 / 60); }; })(); (function animloop() { render(); requestAnimFrame(animloop); })();
两种js实现方案对比:
渲染帧是指浏览器一次完整绘制过程,帧之间的时间间隔是DOM视图更新的最小间隔。由于主流的屏幕刷新率都在60hz,因此渲染一帧的时间就必须控制在16.7ms内才能保证不掉帧。也就是说每一次渲染都要在 16.7ms 内页面才够流畅不会有卡顿感。这段时间内浏览器需要完成如下事情:- js执行:脚本造成了需要重绘的改动,比如增删 DOM、请求动画等
- 样式计算
- 重排和重绘
一个帧内要做这么多事情,如果js执行时间过长超过16.7ms,就会丢掉一次帧的绘制,出现卡顿现象。一般一个帧内的多次DOM改动会被合并渲染。
window.requestAnimationFrame用于在下一个渲染帧之前执行一个回调函数。可以用来做逐帧动画,这会使你的动画函数先于浏览器重绘动作。通常来说,被调用的频率就是60hz。
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
实现一个“沉睡”函数
function sleep(time) { return new Promise((resolve) => { setTimeout(resolve, time) }) }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
数组去重
// 方法一:Set [...new Set([1,1,3,4,2,2])] 或 Array.from(new Set([1,1,3,4,2,2])) // 方法二:filter // 唯一元素的特点:该元素在数组中的索引,与该元素第一次出现时的索引值相同。eg. ['a','b','a'] b的索引是1,b第一次出现时的索引也是1,所以b唯一;但第二个a的索引是2,它第一次出现时的索引却是0,所以a不唯一。 function unique(arr) { return arr.filter((item, index, arr) => { return arr.indexOf(item) === index }) } // 方法三:双循环 function unique(arr) { let res = [arr[0]] for (let i = 1; i < arr.length; i++) { let flag = true for (let j = 0; j < res.length; j++) { if (arr[i] === res[j]) { flag = false break } } if (flag) { res.push(arr[i]) } } return res } // 方法四:对象键值对 function unique(arr) { let res = [] let obj = {} for (let i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { res.push(arr[i]) obj[arr[i]] = 1 } else { obj[arr[i]] += 1 } } return res } // 方法五:检查当前项在后面项中是否包含 function unique(arr) { let res = [] arr.forEach((item, index, arr) => { let rightArr = arr.slice(index+1) if (rightArr.indexOf(item) === -1) { res.push(item) } }) return res }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
数组扁平化
// 方法一:ES6 flat方法 参数默认为1 - 展平一层,Infinity - 无论几层都展成一维。 function flatten(array) { return array.flat(Infinity) } // 方法二:递归 // 普通递归 function flatten(array) { let result = [] array.forEach((item, i) => { if (Array.isArray(item)) { result = result.concat(flatten(item)) } else { result.push(item) } }) return result } // reduce递归 function flatten(array) { return array.reduce((resultArray, currentValue) => { currentValue = Array.isArray(currentValue) ? flatten(currentValue) : currentValue return resultArray.concat(currentValue) }, []) } // 方法三:解构运算符 + concat ... // 利用...可以将最外层数组转为逗号分隔的参数序列 // 利用concat可以实现将多个参数拼接到数组上 [1,2].concat(3, 4) -> [1,2,3,4] function flatten(array) { while (array.some(item => Array.isArray(item))) { array = [].concat(...array) } return array } // 方法四:toString() 利用其能将数组变成以逗号分隔的字符串特性 [1, [2, [3, [4, 5]]]].toString() -> '1,2,3,4,5' 缺点:仅适用纯数字组成的数组 function flatten(array) { let str = array.toString() // '1,2,3,4,5' let strArray = str.split(',') // ['1', '2', '3', '4', '5'] return strArray.map(item => +item) // 隐式类型转换,字符串 -> 数字 } let ar = flatten([1,2,[3,4,[5,6]]]) console.log(ar)
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
- 如何避免重定向?
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
类型检查
js 数据类型:
- 基本类型:Number、String、Boolean、Undefined、Null、Symbol
- 引用类型:Object(Object、Function、Array、Date、RegExp)
typeof判断数据类型:(返回number、string、boolean、undefined、symbol、function、object)
- 对于基本类型,除null外,均可返回正确的结果;
- 对于引用类型,除function外,均返回Object;
- 对于null,返回object;
- 对于function,返回function。
instanceof
用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任意位置
// 实现function myInstanceof(left, right) { let proto = Object.getPrototypeOf(left) let prototype = right.prototype while (true) { if (proto === null) return false // 函数中的 return 会提前终止函数,即便 return 在循环中 if (proto === prototype) return true proto = Object.getPrototypeOf(proto) } }
constructor
对象的 constructor 属性指向 该对象的构造函数,但是这种方式不是很安全,因为 constructor 属性可以被改写toString() 是 Object 的原型方法,调用该方法,默认返回当前数据类型的 [[Class]],格式为[object Xxx]。不仅适用于Object,基本类型也适用。
function getType(data) { let typeClass = Object.prototype.toString.call(data) typeClass = typeClass.replace('[', '').replace(']', '') typeClass = typeClass.split(' ') return typeClass[1] }
undefined 声明了,但是未定义(未赋值),可以使用 void 0 来安全的获取 undefined (表达式 void xx 没有返回值,所以返回的是 undefined) undeclared 未声明,这种变量直接使用时会报引用错误,如 ReferenceError: a is not defined null 代表空对象,常用于初始化对象变量,是个基本类型的值,而 typtof null 为 object 是一个历史问题。
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
找到字符串中第一个不重复的元素
// 正则 当前元素在字符串中只能匹配到一次,则该元素唯一 function getFirstUniqueChar(str) { let targetChar = '' for (let x of str) { let reg = new RegExp(`${x}`, 'g') // 创建正则 if (str.match(reg).length == 1) { targetChar = x break } } return targetChar } // indexOf 目标元素右边的字符串中不含有该元素,则该元素唯一 function getFirstUniqueChar(str) { let targetChar = '' let len = str.length for (let i = 0; i < len; i++) {] let rightStr = str.slice(i+1) if (rightStr.indexOf(str[i]) == -1) { targetChar = str[i] break } } return targetChar }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
实现依次获取urls接口的返回数据
function getAllRes(urls) { let pArr = [] urls.forEach((url) => { let p = new Promise((resolve, reject) => { setTimeout((res) => { console.log(url) resolve(res) }, 200) }) pArr.push(p) }) Promise.all(pArr).then((results) => { console.log(results) }) }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
数组排序(冒泡、快速、插入)
// 冒泡排序(双循环,将比当前元素大的元素,与当前元素交换位置,实现大的放到最前边) function sort(arr) { let len = arr.length for (let i = 0; i < len - 1; i++) { for (let j = i + 1; j < len - 1; j++) { let temp = '' if (arr[j] > arr[i]) { temp = arr[j] arr[j] = arr[i] arr[i] = temp // ES6 [arr[j], arr[i]] = [arr[i], arr[j]] } } } console.log(arr) } // 插入排序(类比摸扑克牌的过程,每摸到一张,都与手中已有的牌比较,根据大小插入相应的位置) function sort(arr) { let handle = [] // 手中的牌 —— 即最终的排序结果 handle.push(arr[0]) let len = arr.length for (let i = 1; i < len; i++) { let A = arr[i] // 最新摸到的一张 let handleLen = handle.length for (let j = handleLen - 1; j >= 0; j--) { // 从手中最后边一张依次向前比较 let B = handle[j] // 手中当前要比较的牌 if (A > B) { // 如果新牌比当前牌大,就放在当前牌后边 handle.splice(j + 1, 0, A) break } if (j === 0) { handle.unshift(A) } } } console.log(handle) } // 快速排序(找出中间位置的元素,比中间元素)(在三者中性能最好) function sort(arr) { let len = arr.length // 结束递归 if (len <= 1) { return arr } // 拿到中间项,并从数组中将中间项去掉(去掉一个后,len变成了len-1) let middleIndex = Math.floor(len / 2) let middleValue = arr.splice(middleIndex, 1) len -= 1 // 建立两个数组,比中间项小的放入leftArr,反之放入rightArr let leftArr = [], rightArr = [] for (let i = 0; i < len; i++) { let item = arr[i] item < middleValue ? leftArr.push(item) : rightArr.push(item) } return sort(leftArr).concat(middleValue, sort(rightArr)) }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
斐波那契数列(1,1,2,3,5,8,13...)
// 递归 function fibonacci(n) { if (n === 0 || n === 1) { // 递归终止条件 return 1 } return fibonacci(n - 1) + fibonacci(n - 2) } // 依次创建每一项 function fibonacci(n) { let arr = [1, 1] for (let i = 2; i <= n; i++) { let currentFb = arr[i - 2] + arr[i - 1] arr.push(currentFb) } return arr[n] }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
输入一个正整数 N,输出所有和为 N 的连续正数序列 15 => [[1,2,3,4,5],[4,5,6],[7,8]]
// 1.向上取整求中位数,则所求结果肯定不含比中位数大的数,例如 15,中位数8,比中位数大的数9,8+9>15 // 2.从1开始,尝试逐一累加中位数以下的数 // 3.连续整数求和公式 (a1+an)*n/2 function fn(N) { let resultArr = [] let middle = Math.ceil(N) // 中位数 for (let i = 1; i <= middle; i++) { for (let j = middle; j > i; j--) { let total = (i+j)*(j-i+1)/2 if (total === N) { let result = createArr(i, j) resultArr.push(result) break } } } console.log(resultArr) } function createArr(i, j) { let arr = [] for (let item = i; item <= j; item++) { arr.push(item) } return arr }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
- Cookie、Session、Token、sessionStorage、localStorage存储位置
- Cookie - 本地硬盘
- Session - 服务器内存
- Token - localStorage中 - 本地硬盘
- sessionStorage - 本地内存 (仅在同一窗口中共享,即便相同协议域名端口下的不同窗口也无法共享)
- localStorage - 本地硬盘 (相同协议域名端口即可共享)
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
base64 转 url
function base64ToUrl(code) { const parts = code.split(';base64,') const type = parts[0].split(':')[1] const decodeStr = window.atob(parts[1]) const decodeStrLen = decodeStr.length const uInt8Array = new Unit8Array(decodeStrLen) for (let i = 0; i < decodeStrLen; i++) { uInt8Array[i] = decodeStr.charCodeAt(i) } const blob = new Blob([uInt8Array], { type }) const url = window.URL.createObjectURL(blob) return url }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
ajax
<!-- 创建对象 --> const xhr = new XMLHttpRequest() <!-- 建立连接 --> xhr.open('get', url, true) <!-- 发送请求 --> xhr.send() <!-- 接收响应 --> xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { success(xhr.responseText) } else { fail(xhr.status) } } }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
实现一个add方法,使计算结果能够满足如下预期: add(1)(2)(3) = 6; add(1, 2, 3)(4) = 10; add(1)(2)(3)(4)(5) = 15;
function add() { var _args = Array.prototype.slice.call(arguments) var _adder = function() { _args.push(...arguments) return _adder } _adder.toString = function() { return _args.reduce(function(a, b) { return a + b }, 0) } return _adder }
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
- 给定一个长度为n的绳子,将其分成m段(m>1),求m段的乘积最大
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
- 函数柯里化?
函数柯里化是指将一个使用多个参数的函数转化为一系列使用一个参数的函数的技术。
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
- 求二进制数
整数:除2取余,倒序读 5 5/2 2 1 2/2 1 0 1/2 0 1 故:5 -> 101
负数:正数求值,取反,再加1
-5
正数 5 -> 101
取反 101 -> 010
加1 010 -> 011
故:-5 -> 011
小数:小数乘2,取整,正序读
0.125
小数乘2 0.125*2=0.25 取整(整数部分为0) 0
0.25*2=0.5 0
0.5*2=1.0 1(乘到小数为0时结束)
正序读 0.001
同理可试求 0.1
0.1*2=0.2 0
0.2*2=0.4 0
0.4*2=0.8 0
0.8*2=1.6 1
0.6*2=1.2 1
0.2*2=0.4 0 (开始循环了)
0.4*2=0.8 0 (开始循环了)
0.8*2=1.6 1 (开始循环了)
...
可见 0.1 求二进制是一个无限循环的小数(0.000110011...),这也是 0.1 + 0.2 != 0.3 的原因。
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
- 原码、反码、补码
正数:原码、反码、补码一致
[+7]原:00000111 [+7]反:00000111 [+7]补:00000111
负数: * 反码:符号位(1)不变,其它位取反 * 补码:反码加1
[-7]原:10000111 [-7]反:11111000 [-7]补:11111001
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
设置小于 12px 的字体
.text { font-size: 12px; // chrome 最小仅支持12px transform: scale(0.5); // 在12px基础上缩小,即便font-size小于12px也是以12px为基础 }
← this 字符串和数组常用方法 →